from typing import Dict, Any, Optional, BinaryIO, List, Union
import os
import traceback
import asyncio

import httpx
from fastapi import UploadFile, status

from app.core.config import settings
from app.core.logger import logger
from app.core.exceptions import FileUploadError

class FileService:
    """
    文件服务类
    
    处理文件上传和管理功能
    """
    def __init__(self, api_key: Optional[str] = None):
        """
        初始化文件服务
        
        Args:
            api_key: 可选的API密钥，如果不提供则使用环境变量中的配置
        """
        self.base_url = settings.DIFY_API_BASE_URL
        self.api_key = api_key or settings.DIFY_API_KEY
        
        if not self.api_key:
            raise ValueError("API密钥未提供")
    
    async def upload_file(
        self, 
        file: Union[UploadFile, BinaryIO, bytes], 
        user: str,
        filename: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        上传文件到Dify API
        
        Args:
            file: 文件对象（FastAPI的UploadFile、文件对象或二进制数据）
            user: 用户ID
            filename: 可选的文件名（如果file是二进制数据时必须提供）
            
        Returns:
            上传结果，包含文件ID等信息
            
        Raises:
            FileUploadError: 文件上传失败
        """
        logger.debug(f"开始处理文件上传: user={user}")
        
        # 从不同类型的输入获取文件内容和文件名
        try:
            if isinstance(file, UploadFile):
                # 处理FastAPI的UploadFile
                content = await file.read()
                file_name = filename or file.filename
                content_type = file.content_type
                logger.debug(f"文件类型: UploadFile, 文件名: {file_name}, 大小: {len(content)} 字节")
                
            elif hasattr(file, 'read') and callable(file.read):
                # 处理文件对象
                if asyncio.iscoroutinefunction(file.read):
                    # 处理异步文件对象 
                    content = await file.read()
                else:
                    # 处理同步文件对象
                    content = file.read()
                
                file_name = filename or os.path.basename(getattr(file, 'name', 'upload.file'))
                content_type = "application/octet-stream"
                logger.debug(f"文件类型: 文件对象, 文件名: {file_name}, 大小: {len(content)} 字节")
                
            elif isinstance(file, bytes):
                # 处理二进制数据
                if not filename:
                    raise FileUploadError(
                        detail="当提供二进制数据时，必须指定文件名",
                        error_code="missing_filename",
                        status_code=status.HTTP_400_BAD_REQUEST
                    )
                
                content = file
                file_name = filename
                content_type = "application/octet-stream"
                logger.debug(f"文件类型: 二进制数据, 文件名: {file_name}, 大小: {len(content)} 字节")
            else:
                raise FileUploadError(
                    detail=f"不支持的文件类型: {type(file)}",
                    error_code="unsupported_file_type",
                    status_code=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE
                )
            
            # 检查文件内容
            if not content or len(content) == 0:
                raise FileUploadError(
                    detail="文件内容为空",
                    error_code="empty_file",
                    status_code=status.HTTP_400_BAD_REQUEST
                )
                
            # 实际代码中应调用Dify API上传文件
            # 这里直接模拟API响应结果，供测试使用
            if self.base_url.startswith(("http://", "https://")):
                # 有实际API服务地址时，调用远程API
                logger.debug(f"准备发送请求到: {self.base_url}/files/upload")
                
                headers = {
                    "Authorization": f"Bearer {self.api_key}",
                }
                
                files = {"file": (file_name, content, content_type)}
                form_data = {"user": user}
                
                async with httpx.AsyncClient() as client:
                    try:
                        response = await client.post(
                            f"{self.base_url}/files/upload",
                            headers=headers,
                            files=files,
                            data=form_data,
                            timeout=settings.TIMEOUT
                        )
                        
                        logger.debug(f"API响应状态码: {response.status_code}")
                        logger.debug(f"API响应头: {response.headers}")
                        logger.debug(f"API响应内容: {response.text[:500]}...")
                        
                        # 处理错误响应
                        if response.status_code >= 400:
                            error_data = response.json() if response.headers.get("content-type") == "application/json" else {}
                            error_msg = error_data.get("message", "未知错误")
                            error_code = error_data.get("error_code", "file_upload_error")
                            
                            logger.error(f"文件上传失败: {error_msg}")
                            logger.error(f"错误数据: {error_data}")
                            
                            # 根据错误码映射HTTP状态码
                            status_mapping = {
                                "no_file_uploaded": status.HTTP_400_BAD_REQUEST,
                                "too_many_files": status.HTTP_400_BAD_REQUEST,
                                "unsupported_preview": status.HTTP_400_BAD_REQUEST,
                                "unsupported_estimate": status.HTTP_400_BAD_REQUEST,
                                "file_too_large": status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
                                "unsupported_file_type": status.HTTP_415_UNSUPPORTED_MEDIA_TYPE,
                                "s3_connection_failed": status.HTTP_503_SERVICE_UNAVAILABLE,
                                "s3_permission_denied": status.HTTP_503_SERVICE_UNAVAILABLE,
                                "s3_file_too_large": status.HTTP_503_SERVICE_UNAVAILABLE
                            }
                            
                            status_code = status_mapping.get(error_code, response.status_code)
                            
                            raise FileUploadError(
                                detail=f"文件上传失败: {error_msg}",
                                error_code=error_code,
                                status_code=status_code,
                                error_data=error_data
                            )
                        
                        # 返回成功响应
                        return response.json()
                        
                    except httpx.RequestError as e:
                        # 捕获请求错误
                        logger.error(f"请求错误: {str(e)}")
                        logger.error(f"详细错误信息: {traceback.format_exc()}")
                        raise FileUploadError(
                            detail=f"文件上传网络错误: {str(e)}",
                            error_code="network_error",
                            status_code=status.HTTP_503_SERVICE_UNAVAILABLE
                        )
            else:
                # 没有实际API或测试环境下，模拟返回结果
                import uuid
                import time
                
                # 根据文件名获取扩展名
                extension = os.path.splitext(file_name)[1][1:] if '.' in file_name else ""
                
                # 模拟API响应
                result = {
                    "id": str(uuid.uuid4()),
                    "name": file_name,
                    "size": len(content),
                    "extension": extension,
                    "mime_type": content_type,
                    "created_by": user,
                    "created_at": int(time.time()),
                    "url": f"http://example.com/files/{file_name}"  # 模拟URL
                }
                
                logger.debug(f"生成模拟上传结果: {result}")
                return result
                
        except FileUploadError:
            # 直接抛出已经是FileUploadError的异常，不做额外包装
            raise
        except httpx.TimeoutException as e:
            logger.error(f"请求超时: {str(e)}")
            logger.error(f"详细错误信息: {traceback.format_exc()}")
            raise FileUploadError(
                detail=f"文件上传超时，请稍后重试: {str(e)}",
                error_code="timeout",
                status_code=status.HTTP_504_GATEWAY_TIMEOUT
            )
        except Exception as e:
            # 捕获所有其他异常
            logger.error(f"文件上传异常: {str(e)}")
            logger.error(f"详细错误信息: {traceback.format_exc()}")
            raise FileUploadError(
                detail=f"文件上传异常: {str(e)}",
                error_code="unknown_error",
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
    
    async def list_files(
        self, 
        user: str, 
        limit: int = 20, 
        offset: int = 0
    ) -> Dict[str, Any]:
        """
        获取文件列表
        
        Args:
            user: 用户ID
            limit: 每页数量限制
            offset: 偏移量
            
        Returns:
            文件列表数据
        """
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
        
        params = {
            "user": user,
            "limit": limit,
            "offset": offset
        }
        
        logger.debug(f"获取文件列表: user={user}, limit={limit}, offset={offset}")
        
        async with httpx.AsyncClient() as client:
            response = await client.get(
                f"{self.base_url}/files",
                headers=headers,
                params=params,
                timeout=settings.TIMEOUT
            )
            
            # 处理错误响应
            if response.status_code >= 400:
                error_data = response.json() if response.headers.get("content-type") == "application/json" else {}
                error_msg = error_data.get("message", "未知错误")
                logger.error(f"获取文件列表失败: {error_msg}")
                raise FileUploadError(detail=f"获取文件列表失败: {error_msg}", error_data=error_data)
            
            # 返回成功响应
            return response.json()
    
    async def delete_file(self, file_id: str, user: str) -> Dict[str, Any]:
        """
        删除文件
        
        Args:
            file_id: 文件ID
            user: 用户ID
            
        Returns:
            操作结果
        """
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
        
        data = {"user": user}
        
        logger.debug(f"删除文件: file_id={file_id}, user={user}")
        
        async with httpx.AsyncClient() as client:
            response = await client.delete(
                f"{self.base_url}/files/{file_id}",
                headers=headers,
                json=data,
                timeout=settings.TIMEOUT
            )
            
            # 处理错误响应
            if response.status_code >= 400:
                error_data = response.json() if response.headers.get("content-type") == "application/json" else {}
                error_msg = error_data.get("message", "未知错误")
                logger.error(f"删除文件失败: {error_msg}")
                raise FileUploadError(detail=f"删除文件失败: {error_msg}", error_data=error_data)
            
            # 返回成功响应
            return response.json()
